#include "XgmmlCreator.h"

char* GetVertexNameById(int id, Vertex* networkVertexs,int networkVertexsSize)
{
	int i;
	for (i=0;i<networkVertexsSize;i++)
	{
		if (networkVertexs[i].id == id)
			return networkVertexs[i].name;
	}
	return "";
}

xmlDocPtr CreateXmlDocForNetwork(Edge* networkEdges,int networkEdgesSize,Vertex* networkVertexs,int networkVertexsSize)
{
	/*Create Doc For Network*/
	xmlDocPtr pXMLDoc;
	xmlNodePtr pRoot;
	xmlNodePtr pNode;
	int i;
	char buf[10];
	char bigEnough[255] = "";
	char* idOneName;
	char* idTwoName;


	pXMLDoc = xmlNewDoc((const xmlChar *)"1.0");

	pRoot = xmlNewNode(NULL,(const xmlChar *)"graph");
	xmlDocSetRootElement(pXMLDoc,pRoot);
	xmlNewProp(pRoot,(const xmlChar *)"xmlns:cy",(const xmlChar *)"http://www.cytoscape.org");
	xmlNewProp(pRoot,(const xmlChar *)"label",(const xmlChar *)"placeHolder");
	xmlNewProp(pRoot,(const xmlChar *)"xmlns:dc",(const xmlChar *)"http://purl.org/dc/elements/1.1/");
	xmlNewProp(pRoot,(const xmlChar *)"xmlns:rdf",(const xmlChar *)"http://www.w3.org/1999/02/22-rdf-syntax-ns#");
	xmlNewProp(pRoot,(const xmlChar *)"xmlns:xlink",(const xmlChar *)"http://www.w3.org/1999/xlink");
	xmlNewProp(pRoot,(const xmlChar *)"xmlns",(const xmlChar *)"http://www.cs.rpi.edu/XGMML");
	xmlNewProp(pRoot,(const xmlChar *)"directed",(const xmlChar *)"0");

	for (i=0;i<networkVertexsSize;i++)
	{
		pNode = NULL;
		pNode = xmlNewChild(pRoot,NULL,(const xmlChar *)"node",NULL);
		xmlNewProp(pNode,(const xmlChar *)"label",(const xmlChar *)networkVertexs[i].name);

		sprintf(buf, "%d", networkVertexs[i].id);
		xmlNewProp(pNode,(const xmlChar *)"id",(const xmlChar *)buf);

		pNode = xmlNewChild(pNode,NULL,(const xmlChar *)"graphics",NULL);
		xmlNewProp(pNode,(const xmlChar *)"type",(const xmlChar *)"ELLIPSE");
		xmlNewProp(pNode,(const xmlChar *)"fill",(const xmlChar *)"placeHolder");
		xmlNewProp(pNode,(const xmlChar *)"width",(const xmlChar *)"5");
		xmlNewProp(pNode,(const xmlChar *)"cy:nodeLabel",(const xmlChar *)networkVertexs[i].name);
		xmlNewProp(pNode,(const xmlChar *)"cy:borderLineType",(const xmlChar *)"solid");
	}

	for (i=0;i<networkEdgesSize;i++)
	{

		idOneName = GetVertexNameById(networkEdges[i].id1,networkVertexs,networkVertexsSize);
		idTwoName = GetVertexNameById(networkEdges[i].id2,networkVertexs,networkVertexsSize);

		bigEnough[0] = '\0';
		strcat(bigEnough, idOneName);
		strcat(bigEnough, " (interaction detected) ");
		strcat(bigEnough, idTwoName);

		pNode = xmlNewChild(pRoot,NULL,(const xmlChar *)"edge",NULL);
		xmlNewProp(pNode,(const xmlChar *)"label",(const xmlChar *)bigEnough);

		sprintf(buf, "%d", networkEdges[i].id1);
		xmlNewProp(pNode,(const xmlChar *)"source",(const xmlChar *)buf);

		sprintf(buf, "%d", networkEdges[i].id2);
		xmlNewProp(pNode,(const xmlChar *)"target",(const xmlChar *)buf);

		pNode = xmlNewChild(pNode,NULL,(const xmlChar *)"graphics",NULL);
		sprintf(buf, "%1.3f", networkEdges[i].weight);
		bigEnough[0] = '\0';
		strcat(bigEnough, "weight=");
		strcat(bigEnough, buf);
		xmlNewProp(pNode,(const xmlChar *)"cy:edgeLabel",(const xmlChar *)bigEnough);
	}


	return pXMLDoc;
}

int CompareCluster(Cluster* a, Cluster* b)
{
	int default_returned_value = 777; /* G: michal, please check logic - we must return a value otherwise we get warning*/

	if (a->size > b->size)
		return 1;

	if (a->size == b->size)
		return 0;

	if (a->size < b->size)
		return -1;

	return default_returned_value;
}

void InsertSorted(ClusterList* list, Cluster* cluster)
{
	ClusterListItem* curr;
	ClusterListItem* prevCurr;
	ClusterListItem* item;

	if (list->head == NULL)
	{
		/*Empty List*/
		list->head = (ClusterListItem*)malloc(sizeof(ClusterListItem));
		list->head->data = cluster;
		list->head->next = NULL;
	}
	else
	{
		curr = list->head;
		prevCurr = NULL;
		while(curr!=NULL && CompareCluster(curr->data,cluster)>0)
		{
			prevCurr = curr;
			curr = curr->next;
		}

		if (curr==NULL)
		{
			/*Item Is Smaller than all List items - add at the end*/
			prevCurr->next = (ClusterListItem*)malloc(sizeof(ClusterListItem));
			prevCurr->next->data = cluster;
			prevCurr->next->next= NULL;
		}
		else
		{
			item = (ClusterListItem*)malloc(sizeof(ClusterListItem));
			item->data = cluster;
			item->next = curr;
			if (prevCurr!=NULL)
			{
				/*Item should be added some where in the middle*/
				prevCurr->next = item;
			}
			else
			{
				/*Item should be added as a new list head (Begining)*/
				list->head = item;
			}
		}
	}
}

void AssignColors(ClusterList* sortedList)
{
	ClusterListItem* curr;
	char* colorsArr[10];
	int currColorIndex;

	colorsArr[0] = "#00FFFF";
	colorsArr[1] = "#0000FF";
	colorsArr[2] = "#8A2BE2";
	colorsArr[3] = "#A52A2A";
	colorsArr[4] = "#7FFF00";
	colorsArr[5] = "#006400";
	colorsArr[6] = "#FFD700";
	colorsArr[7] = "#FF69B4";
	colorsArr[8] = "#FF4500";
	colorsArr[9] = "#C0C0C0";

	currColorIndex = 0;
	curr = sortedList->head;

	while(curr!=NULL)
	{
		curr->data->color = colorsArr[currColorIndex];

		if (currColorIndex < 9)
			currColorIndex++;

		curr = curr->next;
	}
}

xmlNodePtr GetVertextGraphicNodeById(xmlDocPtr pXMLDoc, int vertexId)
{
	xmlNodePtr pRoot;
	xmlNodePtr curr;
	char buf[10];

	sprintf(buf, "%d", vertexId);
	pRoot = xmlDocGetRootElement(pXMLDoc);

	/*Find Vertex Node*/
	curr = pRoot->children;
	while (curr!=NULL)
	{
		if  (strcmp((const char *)curr->name,"node")==0)
		{
			if (strcmp((const char *)xmlGetProp(curr,(const xmlChar *)"id"),(const char *)buf)==0)
			{
				return curr->children;
			}
		}

		curr = curr->next;
	}

	return NULL;
}

void UpdateXmlWithColors(xmlDocPtr pXMLDoc, Cluster* cluster ,char* color)
{
	int i;
	xmlNodePtr pNode;

	for (i=0;i<cluster->size;i++)
	{
		int vertextId=cluster->vertexCollection[i].id;
		pNode = GetVertextGraphicNodeById(pXMLDoc,vertextId);
		xmlSetProp(pNode,(const xmlChar *)"fill",(const xmlChar *)color);
	}
}

void CreateXgmml(xmlDocPtr pXMLDoc, ClusterSet* clusterSet,char* outputPath, int isBest)
{
	int k;
	char buf1[20];
	char bigEnough[300] = "";
	char bigEnough1[300] = "";
	xmlNodePtr pRoot;
	int i;
	ClusterList* list;
	ClusterListItem* tItem;
	ClusterListItem* tItemNext;

	/*Update Label to Root*/
	pRoot = xmlDocGetRootElement(pXMLDoc);
	k = clusterSet->size;

	if (isBest==1)
	{
		sprintf(bigEnough1, "%s", "best_clusters");
	}
	else
	{
		sprintf(bigEnough1, "%d%s", k,"_clustering_solution");
	}


	xmlSetProp(pRoot,(const xmlChar *)"label",(const xmlChar *)bigEnough1);

	/*Sort Clusters by their size*/
	list = (ClusterList*)malloc(sizeof(ClusterList));
	list->head = NULL;

	for (i=0;i<clusterSet->size;i++)
		InsertSorted(list,&(clusterSet->clusterCollection[i]));

	/*Assign Color to each cluster (by Size)*/
	AssignColors(list);

	/*Update Coloring\Graphics Nodes - fill Property*/
	for (i=0;i<clusterSet->size;i++)
		UpdateXmlWithColors(pXMLDoc, &(clusterSet->clusterCollection[i]),clusterSet->clusterCollection[i].color);

	/*Save To File*/
	strcat(bigEnough, outputPath);


	if (isBest)
	{
		strcat(bigEnough, "best_clusters.xgmml");
	}
	else
	{
		k = clusterSet->size;
		sprintf(buf1, "%d", k);
		strcat(bigEnough, buf1);
		strcat(bigEnough, "_clustering_solution.xgmml");
	}

	xmlSaveFileEnc(bigEnough,pXMLDoc,"UTF-8");

	/*FREE*/
	tItem = list->head;
	while (tItem!=NULL)
	{
		tItemNext = tItem->next;
		free(tItem);
		tItem = tItemNext;
	}
	free(list);

}





